package org.mayuanxiaonong.timservice.config;

import java.io.File;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.mayuanxiaonong.timservice.ConfigException;
import org.mayuanxiaonong.timservice.pojo.Host;
import org.mayuanxiaonong.timservice.util.MurMurHash;

/**
 * 服务器主机节点配置
 * 
 * @author 9527
 *
 */
public class HostConfig {

	private static HostConfig instance = null;
	private static SortedMap<Long, Host> tree = null;

	// 配置文件默认文件名
	public static final String DEFAULT_HOST_CONFIG_FILE = "host.conf";

	// 主机节点配置正则验证表达式
	public static final String HOST_REGEX = "\\w+(\\.\\w+)*\\:[1-9]\\d*(\\:[1-9]\\d*)?";

	public static HostConfig getInstance() {
		if (instance == null) {
			synchronized (HostConfig.class) {
				if (instance == null) {
					instance = new HostConfig();
				}
			}
		}
		return instance;
	}

	private HostConfig() {
		tree = new TreeMap<Long, Host>();
		loadHostConfig(null);
	}

	/**
	 * 加载主机节点配置
	 * 
	 * @param configFile
	 */
	private void loadHostConfig(String configFile) {
		File file = new File(SystemConfig.getInstance().getConfDir(),
				configFile == null ? DEFAULT_HOST_CONFIG_FILE : configFile);

		if (!file.exists()) {
			throw new ConfigException("服务器主机节点配置文件不存在,"
					+ file.getAbsolutePath());
		}

		PropertiesConfiguration config = null;
		try {
			config = new PropertiesConfiguration(file);
		} catch (ConfigurationException e) {
			throw new ConfigException("加载服务器主机节点配置文件出错", e);
		}

		int virtualNum = config.getInt("VIRTUAL_NUM", Host.DEFAULT_VIRTUAL_NUM);
		String[] hosts = config.getStringArray("HOST");
		if (hosts == null) {
			return;
		}

		for (String host : hosts) {
			if (host == null) {
				continue;
			}
			if (!host.matches(HOST_REGEX)) {
				throw new ConfigException("主机节点配置格式错误," + host);
			}

			try {
				String[] arr = host.split("\\:", 3);
				String ip = arr[0];
				int port = Integer.parseInt(arr[1]);
				int num = StringUtils.isNotEmpty(arr[2]) ? Integer
						.parseInt(arr[2]) : virtualNum;

				addHost(new Host(ip, port, num));
			} catch (Exception e) {
				throw new ConfigException("解析主机节点配置出错," + host, e);
			}
		}
	}

	/**
	 * 添加主机节点
	 * 
	 * @param host
	 */
	public void addHost(Host host) {
		for (int i = 0; i < host.getNum(); i++) {
			Long hash = MurMurHash.hash(key(host.getIp(), host.getPort(), i));
			tree.put(hash, host);
		}
	}

	/**
	 * 删除主机节点
	 * 
	 * @param host
	 */
	public void removeHost(Host host) {
		for (int i = 0; i < host.getNum(); i++) {
			Long hash = MurMurHash.hash(key(host.getIp(), host.getPort(), i));
			tree.remove(hash);
		}
	}

	/**
	 * 根据key计算一致性hash，并找到命中的主机节点
	 * 
	 * @param key
	 * @return
	 */
	public Host hitHost(String key) {
		Long hash = MurMurHash.hash(key);
		SortedMap<Long, Host> tail = tree.tailMap(hash);
		if (tail.size() == 0) {
			return tree.get(tree.firstKey());
		} else {
			return tree.get(tail.firstKey());
		}
	}

	private String key(String ip, int port, int n) {
		return String.format("SHARD-%s:%d#%d", ip, port, n);
	}

}
